BestCoder Round 93

补题学套路。。

1001 MG loves gold

题意:
$N\le 10^5个数的序列,拆成尽快多的部分,使得每个部分不包含重复数字$

分析:
$直接贪心就好了,每次取尽可能长的不包含重复数字的,set判重即可$

代码:

//
//  Created by TaoSama on 2017-04-01
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, a[N];

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) scanf("%d", a + i);
        int ans = 0;
        set<int> mp;
        for(int i = 1, j; i <= n; i = j) {
            ++ans;
            mp.clear();
            for(j = i; j <= n && !mp.count(a[j]); ++j) mp.insert(a[j]);
        }
        printf("%d\n", ans);
    }

    return 0;
}

1002 MG loves apple

题意:
$给定1个N\le 10^5位的不含前导零的数字,现删去恰好0\le K<N个数字$
$使得剩下的数字,顺序不变,构成的合法数字,能被3整除$
$问是否可行$

分析:
$这个题跟CF EDU 18 C的贪心做法类似$
$首先一个数能被3整除跟数字和sum能被3整除一致$
$接下来就统计一下cnt_i的个,\%3=i的数个数$
$首先特判数字中含0,且k=n-1的情况,CF那个题也是$

$之后就枚举非0数字,使得它作为第一位,存不存在一种合法方案使得sum\%3=0$
$这里要注意,这个非0数字是不能删掉的,他前面的都必须删掉,后面的就枚举一下$
$枚举0,1,3选取的个数,当然是\%3后的,之后判断need的数去掉这些之后能不能被3整除$
$然后判一判就好了,注意细节$
$复杂度O(3^3n)$

//
//  Created by TaoSama on 2017-04-01
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e6 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n, m;
char a[N], r[N];
int cnt[3];

bool go(int mod, int need) {
    for(int a = 0; a < 3; ++a) { //1
        if(a > cnt[1]) continue;
        for(int b = 0; b < 3; ++b) { //2
            if(b > cnt[2]) continue;
            for(int c = 0; c < 3; ++c) { //0
                if(c > cnt[0]) continue;
                if((a + 2 * b) % 3 != mod) continue;
                if(a + b + c > need) continue;
                if((need - a - b - c) % 3 != 0) continue;
                int t = (cnt[1] - a) / 3 + (cnt[2] - b) / 3 + (cnt[0] - c) / 3;
                if(3 * t + a + b + c >= need) return true;
            }
        }
    }
    return false;
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%d%s", &n, &m, a + 1);
        int sum = 0;
        for(int i = 0; i < 3; ++i) cnt[i] = 0;
        for(int i = 1; i <= n; ++i) {
            a[i] -= '0';
            r[i] = a[i] % 3;
            ++cnt[r[i]];
            sum += r[i];
        }

        bool ok = false;
        if(m == n - 1)
            for(int i = 1; i <= n && !ok; ++i) ok |= a[i] == 0;

        int need = m;
        for(int i = 1; i <= n && !ok; ++i) {
            int x = a[i];
            if(x) {
                --cnt[r[i]];
                ok |= go(sum % 3, need);
            } else --cnt[r[i]];
            sum -= r[i];
            if(--need < 0) break;
        }
        puts(ok ? "yes" : "no");
    }

    return 0;
}

1003 MG loves string

题意:
$给定一个26个小写字母的置换A,即进行一次变换,所有字符(‘a’+i)都会变成A_i$
$问一个长度是N\le 10^9随机字符串,变换到自身的期望变换次数$
$输出期望答案乘上26^n以后模10^9+7的结果$

分析:
$可以发现不同的置换的环的长度不超过6个,1+2+3+4+5+6>26$
$所以就枚举不同的置换的环的组合,至少出现一次的方案数$
$我们知道一个置换变回自己的次数是,每个环的长度的lcm$
$先统计出每个环长度的选取的字母的个数$
$f[sta]:=sta状态的环至少出现一次的方案数$
$算这个可以容斥来搞,随便选-非法的$
$g[sta]:=sta状态的环随便选的方案数,g[sta]=cnt[sta]^n$
$f[sta]=g[sta]-\displaystyle\sum_{s0\subset sta} f[s0]$
$之后乘上对应的lcm就可以了$
$复杂度为O(6^3logn)$

//
//  Created by TaoSama on 2017-04-02
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

int n;
char a[27];
int f[1 << 6];

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//  freopen("C:\\Users\\TaoSama\\Desktop\\out.txt","w",stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        scanf("%d%s", &n, a);
        for(int i = 0; i < 26; ++i) a[i] -= 'a';

        map<int, int> mp;
        for(int i = 0; i < 26; ++i) {
            int cnt = 1;
            for(int j = a[i]; j != i; j = a[j]) ++cnt;
            ++mp[cnt];
        }
        vector<pair<int, int>> v(mp.begin(), mp.end());
        auto add = [&](int& x, int y) {if((x += y) >= MOD) x -= MOD;};
        auto quickPow = [&](int x, int y) {
            int ret = 1;
            for(; y; y >>= 1) {
                if(y & 1) ret = 1LL * ret * x % MOD;
                x = 1LL * x * x % MOD;
            }
            return ret;
        };

        int ans = 0;
        for(int s = 1; s < 1 << v.size(); ++s) {
            int sum = 0, lcm = 1;
            for(int i = 0; i < v.size(); ++i) {
                if(s >> i & 1) {
                    lcm = lcm / __gcd(lcm, v[i].first) * v[i].first;
                    sum += v[i].second;
                }
            }
            f[s] = quickPow(sum, n);
            for(int s0 = s & (s - 1); s0; s0 = (s0 - 1) & s) add(f[s], MOD - f[s0]);
            add(ans, 1LL * f[s] * lcm % MOD);
        }
        printf("%d\n", ans);
    }

    return 0;
}

1004 MG loves set

题意:
$如果一个集合所有元素的平方的和小于等于所有元素的和的平方,那么就称这个集合为“和谐集合”。$
$给定n\le 30个数,询问有多少个非空子集是“和谐集合”$

分析:
$现有一个集合S,则题目的条件为\displaystyle\sum_{x\in S} x^2\le (\displaystyle\sum_{x\in S} x)^2$
$移项则有,(\displaystyle\sum_{x\in S} x)^2-\displaystyle\sum_{x\in S} x^2\ge 0,这个就是2\displaystyle\sum_{x, y\in S, x<y}xy\ge 0$
$然后看到n=30,显然的折半枚举$
$令va=\displaystyle\sum_{x\in S} x,vb=2\displaystyle\sum_{x, y\in S, x<y}xy$
$那么上面那个式子由2个集合合并可以表示为,2\times va\times va’+vb+vb’\ge 0$
$把(va, vb)看成直线,(va’, vb’)看成点,那么就是求直线上方的点数,KDT优化即可$
$时间复杂度为O(2^{15}log2^{15})$

//
//  Created by TaoSama on 2017-04-03
//  Copyright (c) 2016 TaoSama. All rights reserved.
//
#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <set>
#include <vector>

using namespace std;
#define pr(x) cerr << #x << " = " << x << "  "
#define prln(x) cerr << #x << " = " << x << endl
const int N = 1e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;

typedef long long LL;
const LL LLINF = 0x3f3f3f3f3f3f3f3fLL;
namespace KDT {
    const int M = 1 << 16, K = 2;
    int D;
    struct Point {
        LL d[K];
        inline LL& operator[](int k) {return d[k];}
        inline const LL& operator[](int k) const {return d[k];}
        inline bool operator<(const Point& p) const {
            return d[D] < p.d[D];
        }
    } a[M];
    struct Node {
        Point key, maxd, mind;
        Node* ch[2];
        int sz;
        inline void up() {
            sz = ch[0]->sz + ch[1]->sz + 1;
            for(int i = 0; i < K; ++i) {
                maxd[i] = max(maxd[i], ch[0]->maxd[i]);
                maxd[i] = max(maxd[i], ch[1]->maxd[i]);
                mind[i] = min(mind[i], ch[0]->mind[i]);
                mind[i] = min(mind[i], ch[1]->mind[i]);
            }
        }
    } pool[M], *ptr, *null, *root;

    inline bool onLine(const Point& p, const Point& q) {
        return 2 * p[0] * q[0] + p[1] + q[1] >= 0;
    }
    inline int h(Node* o, const Point& p) {
        int ret = 0;
        ret += onLine({o->mind[0], o->mind[1]}, p);
        ret += onLine({o->mind[0], o->maxd[1]}, p);
        ret += onLine({o->maxd[0], o->mind[1]}, p);
        ret += onLine({o->maxd[0], o->maxd[1]}, p);
        return ret;
    }

    inline Node* newNode(const Point& p) {
        ptr->key = p;
        ptr->ch[0] = ptr->ch[1] = null;
        for(int i = 0; i < K; ++i)
            ptr->maxd[i] = ptr->mind[i] = ptr->key[i];
        return ptr++;
    }

    void init() {
        ptr = pool;
        null = ptr++;
        null->sz = 0;
        null->ch[0] = null->ch[1] = null;
        for(int i = 0; i < K; ++i) {
            null->mind[i] = LLINF;
            null->maxd[i] = -LLINF;
        }
    }
    void build(Node*& o, int l, int r, int dim) {
        if(l > r) return;
        int m = l + r >> 1;
        D = dim;
        nth_element(a + l, a + m, a + r + 1);
        o = newNode(a[m]);
        build(o->ch[0], l, m - 1, (dim + 1) % K);
        build(o->ch[1], m + 1, r, (dim + 1) % K);
        o->up();
    }
    int query(Node* o, const Point& p) {
        if(o == null) return 0;
        int have = h(o, p);
        if(!have) return 0;
        if(have == 4) return o->sz;

        int ret = onLine(o->key, p);
        ret += query(o->ch[0], p);
        ret += query(o->ch[1], p);
        return ret;
    }
}

int main() {
#ifdef LOCAL
    freopen("C:\\Users\\TaoSama\\Desktop\\in.txt", "r", stdin);
//    freopen("C:\\Users\\TaoSama\\Desktop\\out.txt", "w", stdout);
#endif
    ios_base::sync_with_stdio(0);

    int t; scanf("%d", &t);
    while(t--) {
        int n; scanf("%d", &n);
        vector<int> v(n);
        for(int& x : v) scanf("%d", &x);

        int hf = (n + 1) >> 1;
        for(int s = 0; s < 1 << hf; ++s) {
            LL va = 0, vb = 0;
            for(int i = 0; i < hf; ++i) {
                if(s >> i & 1) {
                    va += v[i];
                    vb += 1LL * v[i] * v[i];
                }
            }
            KDT::a[s + 1] = {va, va* va - vb};
        }

        KDT::init();
        KDT::Node*& root = KDT::root;
        KDT::build(root, 1, 1 << hf, 0);

        n >>= 1;
        int ans = 0;
        for(int s = 0; s < 1 << n; ++s) {
            LL va = 0, vb = 0;
            for(int i = 0; i < n; ++i) {
                if(s >> i & 1) {
                    va += v[hf + i];
                    vb += 1LL * v[hf + i] * v[hf + i];
                }
            }
            ans += KDT::query(root, {va, va * va - vb});
        }
        printf("%d\n", ans - 1);
    }

    return 0;
}